package com.gpj.netty.demo.nio.base;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

/**
 * @className NIOBaseSocketServer
 * @description
 *   基本的Reator模型（即单线程版的NIO模型） -- 服务端
 * @author GPJ
 * @date 2021/2/4 17:14
 * @version 1.0
 **/
@Slf4j
public class NIOBaseSocketServer {
	public static void main(String[] args) throws IOException {
		// 创建一个在本地端口进行监听的服务Socket通道.并设置为非阻塞方式
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		//必须配置为非阻塞才能往selector上注册，否则会报错，selector模式本身就是非阻塞模式
		serverSocketChannel.configureBlocking(false);
		// 绑定本机端口
		serverSocketChannel.socket().bind(new InetSocketAddress(9000));

		// 创建一个选择器selector
		Selector selector = Selector.open();
		// 注册accept事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

		// 轮询监听事件触发
		while (true){
			log.info("等待事件触发...");
			// 阻塞等待事件触发
			selector.select();

			// 获取触发事件的key
			Iterator<SelectionKey> keys = selector.selectedKeys().iterator();

			// 触发事件处理
			while (keys.hasNext()){
				SelectionKey key = keys.next();
				//删除本次已处理的key，防止下次select重复处理
				keys.remove();

				if (key.isAcceptable()){// 建立连接事件
					log.info("【连接建立】事件被触发...");
					ServerSocketChannel serverSC = (ServerSocketChannel) key.channel();
					SocketChannel socketChannel = serverSC.accept();
					socketChannel.configureBlocking(false);
					socketChannel.register(selector, SelectionKey.OP_READ);
				}else if(key.isReadable()){
					log.info("【读】事件被触发...");
					SocketChannel socketChannel = (SocketChannel) key.channel();
					ByteBuffer buffer = ByteBuffer.allocate(1024);
					int len = socketChannel.read(buffer);
					if(len != -1){
						log.info("读取到的请求为:【{}】",
								new String(buffer.array(), 0, len, StandardCharsets.UTF_8));

						// 处理完请求之后进行响应
						socketChannel.write(ByteBuffer.wrap(
								"客户端已接收到请求".getBytes(StandardCharsets.UTF_8)));

						// 重新注册读/写事件监听
						key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
					}else{
						// 无数据则认为连接断开
						socketChannel.close();
					}
				}else if(key.isWritable()){
					log.info("【写】事件被触发...");
					key.interestOps(SelectionKey.OP_READ);
				}else{
					log.warn("未定义事件被触发...");
				}
			}
		}
	}
}
